//#include "stdafx.h"

#include "../BGAudioEngine/stdafx.h"
#include "WavVstHost.h"

CWavVstHost::CWavVstHost(void)
		: module (NULL)
		, mainProc (NULL)
		, effect(NULL)
		, kBlockSize(480)
		, kSampleRate (48000.f)
		, numInputs(0)
		, numOutputs(0)
		, inputs (NULL)
		, outputs (NULL)
		, m_pEditorDlg (NULL)
		, m_nPrograms(0)
{
}

CWavVstHost::~CWavVstHost(void)
{
	Close();
}

//-------------------------------------------------------------------------------------------------------
VstIntPtr VSTCALLBACK HostCallback (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt)
{
	VstIntPtr result = 0;

	// Filter idle calls...
	bool filtered = false;
	if (opcode == audioMasterIdle)
	{
		static bool wasIdle = false;
		if (wasIdle)
			filtered = true;
		else
			wasIdle = true;
	}
	switch (opcode)
	{
		case audioMasterVersion :
			result = kVstVersion;
			break;
	}

	return result;
}

BOOL CWavVstHost::Load(char* fileName)
{
	if (effect)
		Close();
	if((module = LoadLibraryA (fileName))==NULL)
		return FALSE;
	mainProc = (PluginEntryProc)GetProcAddress ((HMODULE)module, "VSTPluginMain");
	if (!mainProc)
		mainProc = (PluginEntryProc)GetProcAddress ((HMODULE)module, "main");
	if(!mainProc){
		FreeLibrary ((HMODULE)module);
		module=NULL;
		mainProc=NULL;
		return FALSE;
	}
	effect = mainProc (HostCallback);
	if(!effect){
		FreeLibrary ((HMODULE)module);
		module=NULL;
		mainProc=NULL;
		effect=NULL;
		return FALSE;
	}
	effect->dispatcher (effect, effOpen, 0, 0, 0, 0);
	SetBuffer((DWORD)kSampleRate,(DWORD)kBlockSize);
	effect->dispatcher (effect, effMainsChanged, 0, 1, 0, 0);
	char t[512]={0},t2[512]={0};
	long i=0;
	do{
		memset(t2,0,512);
		strcpy(t2,t);
		memset(t,0,512);
		effect->dispatcher(effect,effBeginSetProgram,0,0,0,0.0f);
		effect->dispatcher(effect,effSetProgram, 0, i,0,0.0f);
		effect->dispatcher(effect,effEndSetProgram,0,0,0,0.0f);
		effect->dispatcher(effect,effGetProgramName, 0, 0, t,0.0f);
		++i;
	}while(strcmp(t2,t)!=0);
	--i;
	m_nPrograms=i;
	SetProgram(0);
	return TRUE;
}


void CWavVstHost::Close(void)
{
	if(effect){
		effect->dispatcher (effect, effMainsChanged, 0, 0, 0, 0);
		effect->dispatcher (effect, effClose, 0, 0, 0, 0);
		FreeLibrary ((HMODULE)module);
		module=NULL;
		mainProc=NULL;
		effect=NULL;
	}
	if (numInputs > 0)
	{
		for (VstInt32 i = 0; i < numInputs; ++i)
			delete [] inputs[i];
		delete [] inputs;
		numInputs=0;
	}
	if (numOutputs > 0)
	{
		for (VstInt32 i = 0; i < numOutputs; ++i)
			delete [] outputs[i];
		delete [] outputs;
		numOutputs=0;
	}
	m_nPrograms=0;
}

void CWavVstHost::SetBuffer(DWORD SampleRate, DWORD BufferSize, int AvgBitPerSample)
{
	if (numInputs > 0)
	{
		for (VstInt32 i = 0; i < numInputs; ++i)
			delete [] inputs[i];
		delete [] inputs;
	}
	if (numOutputs > 0)
	{
		for (VstInt32 i = 0; i < numOutputs; ++i)
			delete [] outputs[i];
		delete [] outputs;
	}
	kSampleRate=(float)SampleRate;
	kBlockSize=(VstInt32)BufferSize;
	effect->dispatcher (effect, effSetSampleRate, 0, 0, 0, kSampleRate);
	effect->dispatcher (effect, effSetBlockSize, 0, kBlockSize, 0, 0);
	numInputs = effect->numInputs;
	numOutputs = effect->numOutputs;
	if (numInputs > 0)
	{
		inputs = new float*[numInputs];
		for (VstInt32 i = 0; i < numInputs; ++i)
		{
			inputs[i] = new float[kBlockSize];
			memset (inputs[i], 0, kBlockSize * sizeof (float));
		}
	}
	if (numOutputs > 0)
	{
		outputs = new float*[numOutputs];
		for (VstInt32 i = 0; i < numOutputs; ++i)
		{
			outputs[i] = new float[kBlockSize];
			memset (outputs[i], 0, kBlockSize * sizeof (float));
		}
	}
	m_AvgBytePerSample=AvgBitPerSample/8;
}

void CWavVstHost::Process(PVOID pBuffer,DWORD BufferSize)
{
	if(numInputs<1||numOutputs<1)
		return;

	int frameSz=m_AvgBytePerSample*2;
	float l=0.0f,r=0.0f;
	int p=0;

	VstInt32 BufSz=(BufferSize)?BufferSize:kBlockSize;
	unsigned char* t=(unsigned char*)pBuffer;
	for (VstInt32 i = 0; i < numInputs; ++i)
	{
		memset (inputs[i], 0, kBlockSize * sizeof (float));
	}
	for (VstInt32 i = 0; i < numOutputs; ++i)
	{
		memset (outputs[i], 0, kBlockSize * sizeof (float));
	}
	for(int i=0;i<BufSz;++i){
		p=i*frameSz;
		switch(m_AvgBytePerSample)
		{
			case 1:
				l=float((*(char*)(&t[p])));
				r=float((*(char*)(&t[p+m_AvgBytePerSample])));
				break;
			case 2:
				l=float((*(short*)(&t[p])));
				r=float((*(short*)(&t[p+m_AvgBytePerSample])));
				break;
			case 4:
				l=*((float*)(&t[p]));
				r=*((float*)(&t[p+m_AvgBytePerSample]));
				break;
		}
		if(numInputs==2){
			inputs[0][i]=l;
			inputs[1][i]=r;
		}
		else{
			inputs[0][i]=(l+r)/2.0f;
			for(int j=1;j<numInputs;++j){
				inputs[j][i]=inputs[0][i];
			}
		}
	}

	try{
		effect->processReplacing (effect, inputs, outputs, BufSz);
	}
	catch(...){
		return;
	}

	short* ps=NULL;
	float* pf=NULL;
	for(int i=0;i<BufSz;++i){
		p=i*frameSz;
		if(numOutputs==1){
			l=outputs[0][i];
			r=l;
		}
		else{
			l=outputs[0][i];
			r=outputs[1][i];
		}
		switch(m_AvgBytePerSample){
			case 1:
				t[p]=char(l);
				t[p+m_AvgBytePerSample]=char(r);
				break;
			case 2:
				ps=(short*)(&t[p]);
				*ps=short(l);
				ps=(short*)(&t[p+m_AvgBytePerSample]);
				*ps=short(r);
				break;
			case 4:
				pf=(float*)(&t[p]);
				*pf=float(l);
				pf=(float*)(&t[p+m_AvgBytePerSample]);
				*pf=float(r);
				break;
		}
	}
}

void CWavVstHost::Process(float* pLChannel,float* pRChannel,DWORD BufferSize)
{
	if(numInputs<1||numOutputs<1)
		return;

	VstInt32 BufSz=(BufferSize)?BufferSize:kBlockSize;
	for (VstInt32 i = 0; i < numInputs; ++i)
	{
		memset (inputs[i], 0, kBlockSize * sizeof (float));
	}
	for (VstInt32 i = 0; i < numOutputs; ++i)
	{
		memset (outputs[i], 0, kBlockSize * sizeof (float));
	}
	memcpy(inputs[0],pLChannel,sizeof(float)*BufSz);
	memcpy(inputs[1],pRChannel,sizeof(float)*BufSz);

	try{
		effect->processReplacing (effect, inputs, outputs, BufSz);
	}
	catch(...){
		return;
	}

	memcpy(pLChannel,outputs[0],sizeof(float)*BufSz);
	memcpy(pRChannel,outputs[1],sizeof(float)*BufSz);
}

struct MyDLGTEMPLATE: DLGTEMPLATE
{
	WORD ext[3];
	MyDLGTEMPLATE ()
	{
		memset (this, 0, sizeof (*this));
	};
};

void CWavVstHost::OpenEditorDlg(CWnd* hwndParent)
{
	if(m_pEditorDlg){
		m_pEditorDlg->CloseWindow();
		delete m_pEditorDlg;
		m_pEditorDlg=NULL;
	}
	if ((effect->flags & effFlagsHasEditor) == 0)
	{
		return;
	}

	MyDLGTEMPLATE t;	
	t.style = WS_POPUPWINDOW|WS_DLGFRAME|DS_MODALFRAME|DS_CENTER;
	t.cx = 100;
	t.cy = 100;
	
	if(m_pEditorDlg)
	{
		delete m_pEditorDlg;
		m_pEditorDlg=NULL;
	}
	m_pEditorDlg = new CVstDlg;
	m_pEditorDlg->effect=effect;
	m_pEditorDlg->CreateIndirect((LPCDLGTEMPLATE)&t,hwndParent,0);
	m_pEditorDlg->ShowWindow(SW_SHOW);
	return;
}

void CWavVstHost::CloseEditorDlg()
{
	if(m_pEditorDlg){
		m_pEditorDlg->CloseWindow();
		delete m_pEditorDlg;
		m_pEditorDlg=NULL;
	}
}

long CWavVstHost::SetProgram(long n)
{
	if(n<0)
		return -1;
	if(n>m_nPrograms){
		n=m_nPrograms;
		++m_nPrograms;
	}
	effect->dispatcher(effect,effBeginSetProgram,0,0,0,0.0f);
	effect->dispatcher(effect,effSetProgram, 0, n,0,0.0f);
	effect->dispatcher(effect,effEndSetProgram,0,0,0,0.0f);
	return n;
}

void CWavVstHost::SetProgramName(char* Name)
{
	effect->dispatcher(effect,effSetProgramName, 0, 0, Name,0.0f);
}

long CWavVstHost::GetProgram(void)
{
	return effect->dispatcher(effect,effGetProgram, 0, 0, 0,0.0f);;
}

void CWavVstHost::GetProgramName(char* Name)
{
	effect->dispatcher(effect,effGetProgramName, 0, 0, Name,0.0f);
}

long CWavVstHost::GetProgramCount(void)
{
	return m_nPrograms;
}
